#include <linux/vfs/fs.h>
#include <linux/vfs/path.h>
#include <linux/vfs/private/path.h>

extern int shmem_init_fs_context(struct fs_context *fc);

static int rootfs_init_fs_context(struct fs_context *fc)
{
    return shmem_init_fs_context(fc);
}

struct file_system_type rootfs_fs_type = {
    .name = "rootfs",
    .init_fs_context = rootfs_init_fs_context,
    .kill_sb = kill_litter_super,
};

extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
                                       int flags, const char *name,
                                       void *data);

filedesc_t filedesc1;

extern int vfs_init(void);

extern void chrdev_init(void);
extern int tmpfs_init(void);

static void fs_init(void)
{
    tmpfs_init();
}

static int test_fs_init(void)
{
    struct vfsmount *mt;
    struct path root;

    vfs_init();
    chrdev_init();

    filedesc_init(&filedesc1, false);

    fs_init();

    mt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", 0);

    root.mnt = mt;
    root.dentry = mt->mnt_root;

    set_fs_pwd(&filedesc1, &root);
    set_fs_root(&filedesc1, &root);

    return 0;
}
subsys_initcall(test_fs_init);

void init_task_struct_setup_extra(void)
{
    struct task_struct *task = current;

    task->filedesc = &filedesc1;
}

int copy_extra(struct task_struct *orig, struct task_struct *dst, unsigned int flags)
{
    dst->filedesc = orig->filedesc;

    return 0;
}

#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/vfs/api.h>
#include <linux/kdev_t.h>
#include <linux/sprintf.h>
#include <linux/kthread.h>
#include <msh/shell.h>

static int msh_getchar(struct msh_shell *sh)
{
    int ch = 0;

    vfs_read_api(&filedesc1, sh->fd, (char *)&ch, 1);

    return ch;
}

static int msh_print(struct msh_shell *sh, const char *fmt, ...)
{
    va_list args;
    int r;
    char buf[64];

    va_start(args, fmt);
    r = vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    vfs_write_api(&filedesc1, sh->fd, buf, r);

    return r;
}

static bool msh_should_stop(struct msh_shell *sh)
{
    return false;
}

static const struct msh_ops _ops = {
    .getchar = msh_getchar,
    .print = msh_print,
    .should_stop = msh_should_stop,
};

static long stack0[THREAD_SIZE / sizeof(long)];
struct task_struct task0 = {};

static int fair_task(void *arg)
{
    struct msh_shell sh;

    extern const int __fsymtab_start;
    extern const int __fsymtab_end;

    msh_shell_init(&sh, &_ops, (void *)&__fsymtab_start, (void *)&__fsymtab_end);

    sh.fd = vfs_openat_api(&filedesc1, AT_FDCWD, "/ttyS0", O_RDWR, 0);
    msh_shell_run(&sh);

    return 0;
}

static int msh_init(void)
{
    int mode = S_IFCHR;
    int dev = 0;

    dev = MKDEV(4, 0);

    vfs_mknodat_api(&filedesc1, AT_FDCWD, "/ttyS0", mode, dev);

    kthread_run(fair_task, NULL, "shell", 0);

    return 0;
}
late_initcall(msh_init);
